﻿using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Inventories.Services;
using mozhi.smarterp.Business.Products.Repositories;
using mozhi.smarterp.Business.Sales.Repositories;
using Mozhi.Abp.Domain.AuthorCenter;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Business.Sales.Services
{
    public class SaleDeliveryManager : DocumentManagerBase<SaleDelivery, Guid>
    {
        private ICustomerRepository _customerRepository;
        private IEmployeeRepository _employeeRepository;
        private ICurrencyRepository _currencyRepository;
        private IDepartmentRepository _departmentRepository;
        private IProductAuxiliaryUnitRepository _auxiliaryUnitRepository;
        private IProductRepository _productRepository;
        private ITaxRateRepository _taxRateRepository;
        private IUnitRepository _unitRepository;
        private IGuidGenerator _guidGenerator;
        private IWarehouseRepository _warehouseRepository;
        private IOrganizationRepository _organizationRepository;
        private ISaleDeliveryReceivableRepository _deliveryReceivableRepository;
        private InventoryTransationManager _inventoryTransationManager;
        private IProductInventorySettingRepository _inventorySettingRepository;

        public SaleDeliveryManager(ISaleDeliveryRepository repository, INumberGeneratorRepository numberGeneratorRepository, IEmployeeRepository employeeRepository, ICurrencyRepository currencyRepository, IDepartmentRepository departmentRepository,
            IProductRepository productRepository, ITaxRateRepository taxRateRepository, IProductAuxiliaryUnitRepository auxiliaryUnitRepository, IUnitRepository unitRepository, IGuidGenerator guidGenerator , IWarehouseRepository warehouseRepository, 
            IOrganizationRepository organizationRepository , ISaleDeliveryReceivableRepository deliveryReceivableRepository , InventoryTransationManager inventoryTransationManager , IProductInventorySettingRepository inventorySettingRepository)
            : base(repository, numberGeneratorRepository, DocumentType.SaleDelivery)
        {
            _guidGenerator = guidGenerator;
            _employeeRepository = employeeRepository;
            _currencyRepository = currencyRepository;
            _departmentRepository = departmentRepository;
            _taxRateRepository = taxRateRepository;
            _productRepository = productRepository;
            _auxiliaryUnitRepository = auxiliaryUnitRepository;
            _unitRepository = unitRepository;
            _warehouseRepository = warehouseRepository;
            _organizationRepository = organizationRepository;
            _deliveryReceivableRepository = deliveryReceivableRepository;
            _inventoryTransationManager = inventoryTransationManager;
            _inventorySettingRepository = inventorySettingRepository;
        }


        public async Task<SaleDelivery> GetAsync(Guid? id, Guid customerId, DateTime date, Guid? departmentId, Guid? warehouseClerkId, Guid currencyId, decimal exchangeRate, string comment)
        {
            var customer = await _customerRepository.GetAsync(customerId);
            var currency = await _currencyRepository.GetAsync(currencyId);
            var department = departmentId.HasValue ? await _departmentRepository.GetAsync(departmentId.Value) : null;
            var warehouseClerk = warehouseClerkId.HasValue ? await _employeeRepository.GetAsync(warehouseClerkId.Value) : null;

            currency.SetExchangeRate(exchangeRate);

            SaleDelivery delivery;

            if (id.HasValue)
            {
                delivery = await _repository.GetAsync(id.Value);

                delivery.Change(date, customer, currency, department, warehouseClerk, comment);
            }
            else
            {
                var number = await GetNextNumberAsync();

                delivery = new SaleDelivery(_guidGenerator.Create(), number, date, customer, currency, department, warehouseClerk, comment);
            }

            return delivery;
        }

        public async Task<SaleDeliveryItem> GetItemAsync(Guid? id, Guid productId, Guid inventoryUnitId, decimal orderedQuantity, decimal deliveredQuantity, decimal price, Guid? taxRateId, Guid warehouseId, Guid? locationId,
            string batchNumber, string serialNumber, DateTime? productionDate,  string remark, bool isGift)
        {
            var inventorySetting = await _inventorySettingRepository.GetByProductIdAsync(productId);

            if (inventorySetting == null)
                throw new UserFriendlyException("产品未设置库存信息", "100000");

            inventorySetting.ValidateProductInventory(deliveredQuantity, batchNumber, serialNumber, productionDate);

            var product = await _productRepository.GetAsync(productId);

            var productAuxiliaryUnit = inventoryUnitId == product.BasicUnitId ? null : await _auxiliaryUnitRepository.GetAsync(productId, inventoryUnitId);

            var inventoryUnit = await _unitRepository.GetAsync(inventoryUnitId);

            var taxRate = taxRateId.HasValue ? await _taxRateRepository.GetAsync(taxRateId.Value) : null;

            var warehouse = await _warehouseRepository.GetAsync(warehouseId);

            id = id ?? _guidGenerator.Create();

            var item = new SaleDeliveryItem(id.Value, product, inventoryUnit, orderedQuantity, deliveredQuantity, price, productAuxiliaryUnit, taxRate,
                warehouse, locationId, batchNumber, serialNumber, productionDate, inventorySetting.ShelfLifeDays , remark, isGift);

            return item;
        }


        public async Task UpdateStatusAsync(Guid id, DocumentStatusType status)
        {
            var delivery = await _repository.GetAsync(id);

            delivery.SetStatus(status);

            //审核
            if (status == DocumentStatusType.PendingApproval || status == DocumentStatusType.Approved)
            {
                await ValidateDocumentDateAsync(delivery.Date);
            }

            //撤销审核要满足几个条件 1：没有下推应收款时，可用库存数量是要足够的，2：下推了应付款，需要先删除应付款单
            if (delivery.Status == DocumentStatusType.Approved && status == DocumentStatusType.PendingApproval)
            {
                var receivableCount = await _deliveryReceivableRepository.GetCountAsync(delivery.Id);

                if (receivableCount > 0)
                    throw new UserFriendlyException($"销售出库单：{delivery.Number}已关联应收单，不允许反审核", "100000");

                await HandleUnApproveAsync(delivery);

            }
            else if (delivery.Status == DocumentStatusType.PendingApproval && status == DocumentStatusType.Approved)
            {
                await HandleApproveAsync(delivery);
            }

            await _repository.UpdateAsync(delivery);

        }

        #region
        private async Task HandleApproveAsync(SaleDelivery delivery)
        {
            var items = delivery.Items;

            if (items == null || items.Count == 0)
                throw new UserFriendlyException($"销售出库单{delivery.Number}为没有分录", "100000");

            foreach (var item in items)
            {
                var inventoryFlowType = delivery.IsCreditNote ? InventoryFlowType.Inbound : InventoryFlowType.Outbound;

                var transaction = await _inventoryTransationManager.GetAsync(inventoryFlowType, DocumentType.PurchaseReceipt, delivery.Id, item.Id, delivery.Date,
                    item.ProductId, item.WarehouseId, item.LocationId, item.BatchNumber, item.SerialNumber, item.ProductionDate, item.ExpirationDate, item.BasicQuantity, true);

                await _inventoryTransationManager.CalculateInventoryRealTimesAsync(transaction);
            }
        }

        private async Task HandleUnApproveAsync(SaleDelivery delivery)
        {
            var items = delivery.Items;

            if (items == null || items.Count == 0)
                throw new UserFriendlyException($"销售出库单{delivery.Number}为没有分录", "100000");

            foreach (var item in items)
            {
                await _inventoryTransationManager.DeleteAsync(delivery.Id, item.Id, true);
            }
        }

        private async Task ValidateDocumentDateAsync(DateTime date)
        {
            var organization = await _organizationRepository.GetAsync();

            if (organization == null)
                throw new BusinessException("无法获取到组织信息", "100000");

            organization.ValidateDocumentDate(date);
        }
        #endregion
    }
}
